
<!DOCTYPE HTML>
<html>
<head>
<title>Canvas tutorial</title>
<style type="text/css">
    body, ul{ margin:0;padding:0; }
    body{ font-size:12px;background-color:#646464; }
    ul{ list-style-type:none; }
    canvas{ cursor:default; }
    .tools{ }
    .tools li{ margin:0 10px;float:left; }
    .pencil{ background:url(img/pencil.jpg) no-repeat; }
    .barrel{ background:url(img/barrel.jpg) no-repeat; }
    .text{ background:url(img/text.jpg) no-repeat; }
    .eraser{ background:url(img/eraser.jpg) no-repeat; }
    .straw{ background:url(img/straw.jpg) no-repeat; }
    .magnifier{ background:url(img/magnifier.jpg) no-repeat; }

    .module{
        -moz-border-radius:5px;
        -webkit-border-radius:5px;
        border-radius:5px;
        background-color:#1C1C1C;
        -moz-box-shadow: 0 5px 10px #1C1C1C;
        -webkit-box-shadow: 0 5px 10px #1C1C1C;
        box-shadow: 0 5px 10px #1C1C1C;
        opacity:0.95;
        filter:alpha(opacity=95);
        color:#fff;
        z-index:10;
        cursor:default;
    }
    .module>div:nth-child(1){
        padding:5px 10px 0 10px;
        height:20px;
        -moz-border-radius:5px;
        -webkit-border-radius:5px;
        border-radius:5px;
        background:-moz-linear-gradient(top,#303030,#1C1C1C);
        background:-webkit-gradient(linear,0 0,0 100%,from(#303030),to(1,#1C1C1C));
        font-size:14px;
    }
    .module>div:nth-child(1)>span{
        font-family:Arial;
        color:#808080;
        font-size:12px;
        float:right;
        font-weight:bold;
    }
    .module>div:nth-child(1)>span:hover{
        color:#fff;
    }
    .module>div:nth-child(2){
        overflow:hidden;
        padding:10px;
    }
    .rangeBar{
        margin:7px 0;
        width:120px;
        height:4px;
        background-color:#7E7E7B;
        background:-moz-linear-gradient(top,#6C6E6E,#808183);
        background:-webkit-gradient(linear,0 0,0 100%,from(#6C6E6E),to(#808183));
        -moz-border-radius:3px;
        -webkit-border-radius:3px;
        border-radius:3px;
        border:1px solid #646464;
        float:left;
    }
    .rangeBar div{
        width:13px;height:15px;background:url(img/range_cur.png) no-repeat;position:relative;left:-6px;top:-4px;
    }
</style>
<script type="text/javascript" src="js/jquery-1.4.js"></script>
<script type="text/javascript" src="js/Tool.js"></script>
<script type="text/javascript" src="js/GUI.js"></script>
<script type="text/javascript">
/*
 // Canvas2D : v0.3 : 2009.01.16
 --------------------------------
 1) Retrofits older browsers that don't support HTML 5.0
 * IECanvas, an ActiveX module which emulates <canvas>, retrofits IE: code.google.com/p/iecanvas/
 * Retrofits prototyping up to Safari 3.  IE is fixed externally in /lib/HTMLElement.htc
 * Retrofits arcTo up to (which browsers don't work???)
 * Retrofits fillText / strokeText up to Safari 4, Firefox 3.1, and Opera
 * Retrofits transform / setTransform up to Safari 4, Firefox 3.1 (+?)

 2) Parses and Implements SVG:
 * Parses <font> attributes and paths then converts to FOB for fast access.  In the process of emulating more attributes in <canvas>.
 * Parses <path> [ M, m, L, l, C, c, Q, q, Z, H, h, V, v, A, a ] and converts to VOB [ M, L, C, Q, Z ] for fast access
 * Implements Shapes: circle, ellipse, line, path, polygon, polyline, rect

 3) Adds non-standardized features:
 * Pixel: colorMatrix, colorTransform (browser must support getImageData / putImageData)
 * Path: arc, roundRect, star, polygon, burst, arrow, wedge, gear, spiral, superformula, drawVector, drawVOB
 * Typeface: drawText


 * Breaking down the libraries:
 -------------------------------
 Canvas2D.js // add, remove, fix (browser fixes), ect
 Canvas2D.Pixel.js // ColorMatrix + ConvolutionMatrix + CustomFilter mixing and applying (maybe a few other default filters, such as DisplacementMap)
 Canvas2D.Raster.js // reflect, straighten, flip, rotate, resize
 Canvas2D.Record.js // sets up functions for recording native functions
 Canvas2D.StyleSheet.js // importing, exporting, converting, and storing:  patterns, gradients, colors, and native styles (i.e. miterLimit, font, ect)
 Canvas2D.SVG.js // parse SVG into VOB (fonts and paths)
 Canvas2D.Transform.js // geometric TransformationMatrix + Decompose
 Canvas2D.Typeface.js // text building and styles
 Canvas2D.Vector.js // shapes and broken native path functions (optionally caches in VOB)
 Canvas2D.VOB.js // v2.0 is pretty complex, ties in layers + groups w/ vector objects, so gets it's own .js


 * Listing functions supported by the library:
 ----------------------------------------------
 Canvas2D.add // create new <canvas> element
 Canvas2D.remove // remove specific <canvas> element


 HTMLCanvasElement (HTML 5.0)
 -----------------------------
 attribute unsigned long width;
 attribute unsigned long height;

 DOMString toDataURL([Optional] in DOMString type); // converts <canvas> in a base64 string of image type
 DOMObject getContext(in DOMString contextId); // returns CanvasRenderingContext2D


 State Stack (HTML 5.0)
 --------------------------
 void save(); // push state on state stack
 void restore(); // pop state stack and restore state

 void getPath(); // returns current path *


 Style API
 -----------------------
 Composition (HTML 5.0)
 -----------------------
 attribute float globalAlpha; // (default 1.0)
 attribute DOMString globalCompositeOperation; // (default source-over)

 Colors & Styles (HTML 5.0)
 --------------------------
 attribute any strokeStyle; // (default black)
 attribute any fillStyle; // (default black)

 Line Caps & Joins (HTML 5.0)
 ----------------------------
 attribute float lineWidth; // (default 1)
 attribute DOMString lineCap; // "butt", "round", "square" (default "butt")
 attribute DOMString lineJoin; // "round", "bevel", "miter" (default "miter")
 attribute float miterLimit; // (default 10)

 Shadows: supported in Firefox 3.1, Safari 3, Opera 10
 -----------------------------------------
 attribute float shadowOffsetX; // (default 0)
 attribute float shadowOffsetY; // (default 0)
 attribute float shadowBlur; // (default 0)
 attribute DOMString shadowColor; // (default transparent black)

 CanvasGradient (HTML 5.0)
 -------------------------
 createLinearGradient(float x0, float y0, float x1, float y1);
 createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1);

 interface { void addColorStop(float offset, DOMString color); };


 Patterns (HTML 5.0)
 -------------------
 CanvasPattern createPattern(HTMLImageElement image, DOMString repetition);
 CanvasPattern createPattern(HTMLCanvasElement image, DOMString repetition);


 Transformation API (HTML 5.0)
 ------------------------------
 void scale(float x, float y);
 void rotate(float angle);
 void translate(float x, float y);
 void transform(float m11, float m12, float m21, float m22, float dx, float dy);
 void setTransform(float m11, float m12, float m21, float m22, float dx, float dy);


 Pixel API (HTML 5.0)
 ------------------------
 ImageData createImageData(float sw, float sh);
 ImageData getImageData(float sx, float sy, float sw, float sh); // Opera 9.5+, Firefox 2.0+, Safari 4.0+
 void putImageData(ImageData imagedata, float dx, float dy);
 void putImageData(ImageData imagedata, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight);

 Addon: Filters (seperate file contains filters, cause this is a lot of information)
 void getPixel();
 void setPixel();


 Raster API (HTML 5.0)
 ------------------------
 void drawImage(HTMLImageElement image, float dx, float dy, [Optional] float dw, float dh);
 void drawImage(HTMLImageElement image, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh);
 void drawImage(HTMLCanvasElement image, float dx, float dy, [Optional] float dw, float dh);
 void drawImage(HTMLCanvasElement image, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh);


 Vector API (HTML 5.0)
 ----------------------
 boolean isPointInPath(float x, float y);

 void beginPath();
 void closePath();
 void moveTo(float x, float y);
 void lineTo(float x, float y);
 void arcTo(float x1, float y1, float x2, float y2, float radius); // don't punish for not having arcTo native
 void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y);
 void quadraticCurveTo(float cpx, float cpy, float x, float y);
 void arc(float x, float y, float radius, float startAngle, float endAngle, boolean anticlockwise); // openlaszlo.org/jira/browse/LPP-3491
 void rect(float x, float y, float w, float h);
 void clearRect(float x, float y, float w, float h);
 void fillRect(float x, float y, float w, float h);
 void strokeRect(float x, float y, float w, float h);
 void fill();
 void stroke();
 void clip();

 // Addon: SVG Shapes @ w3.org/TR/SVG11/shapes.html


 void circle(float cx, float cy, float r) // defines a circle based on a center point and a radius
 void ellipse(float cx, float cy, float rx, float ry) // defines an ellipse based on a center point and two radii
 void line(float x1, float y1, float x2, float y2) // defines a line segment that starts at one point and ends at another
 void path(string d) // SVG instructions, contains the moveto, line, cubic, quadratic, arc, and closepath: "M5,5 C5,45 45,45 45,5"

 // Addon: Path Shapes

 void roundRect(float x, float y, float width, float height, float rad) // circular rounded-rectangle
 void roundRect(float x, float y, float width, float height, float rx, float ry) // elliptical rounded-rectangle (two radii)
 void wedge(float x, float y, float radius, float startAngle, float endAngle, boolean anticlockwise); // en.wikipedia.org/wiki/Wedge_%28geometry%29
 void polygon(x, y, radius, sides) // en.wikipedia.org/wiki/Regular_polygon
 void gear(float x, float y, float radius, int sides, float slope); // en.wikipedia.org/wiki/Gear
 void star(float x, float y, float radius, int sides, float slope);
 void burst(float x, float y, float radius, int sides, float slope);
 void spiral(x, y, radius, sides, coils) // en.wikipedia.org/wiki/Archimedean_spiral
 void superformula(x, y, radius, points, m, n1, n2, n3) // en.wikipedia.org/wiki/Superformula


 Typeface API (HTML 5.0) -- Firefox 3.1+ and Safari 4.0+
 ------------------------
 attribute DOMString font; // (default 10px sans-serif)
 attribute DOMString textAlign; // "start", "end", "left", "right", "center" (default: "start")
 attribute DOMString textBaseline; // "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" (default: "alphabetic")

 void fillText(DOMString text, float x, float y, [Optional] float maxWidth);
 void strokeText(DOMString text, float x, float y, [Optional] float maxWidth);
 void measureText(DOMString text); // returns { width: 100 }

 // Addon: Mozilla

 void drawText(DOMString text, float x, float y, [Optional] float maxWidth); // rhino-canvas.sourceforge.net/www/drawstring.html

 */
var tool,canvasOffsetX, canvasOffsetY, canvasPos = { x: 0, y: 0 }, toolName, startX, startY;

$(function () {

    var paint = new Module({
        title: "画板",
        contents: ['<canvas id="c" width="700" height="575" style="position: absolute; z-index: 0; background: url(img/op_8x8.gif);cursor:crosshair;"></canvas>\
                    <canvas id="c_temp" width="700" height="575" style="position: absolute; z-index: 1;"></canvas>\
                    <canvas id="c_brush" width="1" height="1"></canvas>'],
        width: 720,
        height: 620
    });

    center(paint.M[0]);

    canvas = {};
    canvas.main = $("#c");
    if (!canvas.main[0].getContext) {
        alert("您的浏览器不支持此应用");
        return;
    }

    var tools = new Module({
        title: "工具",
        contents: ['<div><img src="img/Pencil_2.png" onclick="tool.pencil.load();" style="width:40px;height:40px;"/>\
                    <img src="img/Brush_2.png" onclick="tool.brush.load();" style="width:40px;height:40px;"/>\
                    <img src="img/Eraser_2.png" onclick="tool.eraser.load();" style="width:40px;height:40px;"/>\
                    <img src="img/Picker_2.png" onclick="tool.picker.load();canvas.temp[0].style.cursor=\'url(img/picker_2.png),pointer\';" style="width:40px;height:40px;"/>\
                    <span onclick="tool.beeline.load();canvas.temp[0].style.cursor=\'crosshair\';" >直线</span>\
                    <img src="img/Shape_ellipses_2.png" onclick="tool.rotundity.load();canvas.temp[0].style.cursor=\'crosshair\';" style="width:40px;height:40px;"/>\
                    <img src="img/Shape_polygon_2.png" onclick="tool.rectangle.load();canvas.temp[0].style.cursor=\'crosshair\';" style="width:40px;height:40px;"/></div>\
                    <div id="color" style="margin-top:10px;margin-left:5px;width: 34px; height: 23px; border: 1px solid #fff; float: left;"></div>\
                    <div onclick="clearPaint();" style="margin-left:10px;cursor:pointer;font-weight:bold;line-height:40px;font-size:20px;float:left;">重画</div>\
                    <div onclick="location.href=canvas.main[0].toDataURL();" style="margin-left:10px;cursor:pointer;font-weight:bold;line-height:40px;font-size:20px;float:left;">保存</div>']
    });
    tools.M.css({ left: Math.max(0, paint.M[0].offsetLeft - tools.M[0].offsetWidth - 10) + "px", top: paint.M[0].offsetTop + "px" });

    m_option = new Module();
    m_option.M.css({ left: Math.max(0, paint.M[0].offsetLeft - m_option.M[0].offsetWidth - 10) + "px", top: paint.M[0].offsetTop + tools.M[0].offsetHeight + 10 + "px" });

    var palette = new Module({
        title: "调色板",
        x: 500,
        y: 200,
        contents: [
            '<canvas id="c_palette" width="120" height="18"></canvas>'
        ],
        onload: function () {
            var $c_palette = $("#c_palette");
            var w = $c_palette.width(), h = $c_palette.height();

            var ctx_palette = $c_palette[0].getContext("2d");
            var l = ctx_palette.createLinearGradient(0, 0, w, 0);
            l.addColorStop(0.125, '#FF0000');
            l.addColorStop(0.25, '#FFFF00');
            l.addColorStop(0.375, '#008080');
            l.addColorStop(0.5, 'blue');

            l.addColorStop(0.625, '#800080');
            l.addColorStop(0.75, '#2F4F4F');
            l.addColorStop(0.875, '#191970');
            l.addColorStop(1, '#000000');
            ctx_palette.fillStyle = l;
            ctx_palette.fillRect(0, 0, w, h);
            ctx_palette.stroke();

            $c_palette
                    .mousemove(function (e) {
                var x = (e.layerX || e.offsetX) - 1, y = (e.layerY || e.offsetY) - 1;
                if (x < 0) x = 0;
                else if (x >= w) x = w - 1;
                if (y < 0) y = 0;
                else if (y >= h) y = h - 1;

                var data = ctx_palette.getImageData(x, y, 1, 1);
                var r = data.data;
                r[3] = r[3] / 255;
                var rgba = "rgba(" + (Sys.firefox ? r.toString() : r[0] + "," + r[1] + "," + r[2] + "," + r[3]) + ")";

            })
                    .mousedown(function (e) {
                        var x = (e.layerX - 10 || e.offsetX) - 1, y = (e.layerY || e.offsetY) - 1;
                        if (x < 0) x = 0;
                        else if (x >= w) x = w - 1;
                        if (y < 0) y = 0;
                        else if (y >= h) y = h - 1;

                        var data = ctx_palette.getImageData(x, y, 1, 1);
                        var r = data.data;
                        r[3] = r[3] / 255;
                        var rgba = "rgba(" + (Sys.firefox ? r.toString() : r[0] + "," + r[1] + "," + r[2] + "," + r[3]) + ")";

                        tool.fillStyle = rgba;
                        canvas.temp.ctx.fillStyle = rgba;
                        canvas.temp.ctx.strokeStyle = rgba;
                        fillBrush();
                        $("#color").css("backgroundColor", rgba);
                    });
        }
    });

    canvas = {};
    canvas.main = $("#c");
    if (!canvas.main[0].getContext) {
        alert("您的浏览器不支持此应用");
        return;
    }
    canvas.temp = $("#c_temp");
    canvas.brush = $("#c_brush");
    canvas.main.ctx = canvas.main[0].getContext("2d");
    canvas.temp.ctx = canvas.temp[0].getContext("2d");
    canvas.brush.ctx = canvas.brush[0].getContext("2d");

    canvas.main.ctx.fillStyle = "#fff";
    canvas.main.ctx.fillRect(0, 0, canvas.temp.width(), canvas.temp.height());

    tool = {
        fillStyle: "#000",
        pencil: new Tool({
            type: "pencil",
            diameter: 10,
            opacity: 80,
            draw: function (x, y) {
                draw(this.options.ctx, this.options.diameter, x + 7, y);
            }
        }),
        brush: new Tool({
            type: "brush",
            diameter: 25,
            hardness: 60,
            flow: 92,
            opacity: 70,
            draw: function (x, y) {
                draw(this.options.ctx, this.options.diameter, x + 7, y);
            }
        }),
        eraser: new Tool({
            type: "eraser",
            ctx: canvas.main.ctx,
            diameter: 25,
            hardness: 60,
            flow: 90,
            opacity: 30,
            draw: function (x, y) {
                var c = this.options.ctx;
                c.globalCompositeOperation = 'destination-out';
                draw(c, this.options.diameter, x + 7, y + 10);
                c.globalCompositeOperation = 'source-over';
            }
        }),
        picker: new Shape({
            type: "picker",
            down: function (e) {
                var x = (e.layerX - 10 || e.offsetX) - 1, y = (e.layerY || e.offsetY) - 1;
                if (x < 0) x = 0;
                else if (x >= canvas.main.width()) x = canvas.main.width() - 1;
                if (y < 0) y = 0;
                else if (y >= canvas.main.height()) y = canvas.main.height() - 1;

                var data = canvas.main.ctx.getImageData(x + 15, y + 35, 1, 1);
                var r = data.data;
                r[3] = r[3] / 255;
                var rgba = "rgba(" + (Sys.firefox ? r.toString() : r[0] + "," + r[1] + "," + r[2] + "," + r[3]) + ")";

                tool.fillStyle = rgba;
                canvas.temp.ctx.fillStyle = rgba;
                canvas.temp.ctx.strokeStyle = rgba;
                fillBrush();
                $("#color").css("backgroundColor", rgba);
            }
        }),
        beeline: new Shape({
            type: "beeline",
            move: function (e) {
                var ctx = canvas.temp.ctx;
                ctx.clearRect(0, 0, canvas.temp.width(), canvas.temp.height());   //清除画布
                ctx.beginPath();
                ctx.moveTo(startX - 10, startY - 35);                         //移动画笔至当前鼠标坐标
                ctx.lineTo(canvasPos.x - 10, canvasPos.y - 35);               //绘制线条至当前鼠标坐标
                ctx.stroke();
            }
        }),
        rectangle: new Shape({
            type: "rectangle",
            move: function (e) {
                var ctx = canvas.temp.ctx;
                ctx.clearRect(0, 0, canvas.temp.width(), canvas.temp.height());
                ctx.strokeRect(startX - 10, startY - 35, canvasPos.x - startX, canvasPos.y - startY);
            }
        }),
        rotundity: new Shape({
            type: "rotundity",
            move: function (e) {
                var ctx = canvas.temp.ctx;
                ctx.clearRect(0, 0, canvas.temp.width(), canvas.temp.height());
                ctx.beginPath();
                ctx.arc(startX - 10, startY - 35, Math.abs(canvasPos.x - startX), 0, Math.PI * 2, true);
                ctx.stroke();
            }
        })
    }

    tool.pencil.load();
    palette.M.css({ left: Math.max(0, paint.M[0].offsetLeft - palette.M[0].offsetWidth - 10) + "px", top: paint.M[0].offsetTop + m_option.M[0].offsetHeight + tools.M[0].offsetHeight + 94 + "px" });

    var clientRect = canvas.temp[0].getBoundingClientRect();

    canvasOffsetX = clientRect.left, canvasOffsetY = clientRect.top;

    canvas.temp.mousedown(function (e) {
        canvas.temp.down = 1;                       //标记鼠标down
        startX = canvasPos.x, startY = canvasPos.y; //记录开始点坐标

        tool.current.down(e);
        e.preventDefault();

    });

    document.onmousemove = function (e) {
        updatePos(e);   //更新鼠标坐标
        if (canvas.temp.down) {
            tool.current.move(e);
        }
    }
    document.onmouseup = function (e) {
        if (canvas.temp.down) {
            updateImage();          //更新画布
            canvas.temp.down = 0;   //标记鼠标up
        }
    };
});
</script>
</head>
<body>
</body>
</html>